/*
 * ProtocolParser.cpp
 *
 *  Created on: Sep 7, 2017
 */
#include <algorithm>
#include <vector>
#include <array>
#include <string.h>
#include <system/Mutex.h>
#include "CommDef.h"
#include "uart/ProtocolParser.h"
#include "utils/Log.h"
#include "uart/UartContext.h"
#include "uart/upgradeFile.h"

static Mutex sLock;
static std::vector<OnProtocolDataUpdateFun> sProtocolDataUpdateListenerList;

void registerProtocolDataUpdateListener(OnProtocolDataUpdateFun pListener) {
	Mutex::Autolock _l(sLock);
	LOGD("registerProtocolDataUpdateListener\n");
	if (pListener != NULL) {
		sProtocolDataUpdateListenerList.push_back(pListener);
	}
}

void unregisterProtocolDataUpdateListener(OnProtocolDataUpdateFun pListener) {
	Mutex::Autolock _l(sLock);
	LOGD("unregisterProtocolDataUpdateListener\n");
	if (pListener != NULL) {
		std::vector<OnProtocolDataUpdateFun>::iterator iter = sProtocolDataUpdateListenerList.begin();
		for (; iter != sProtocolDataUpdateListenerList.end(); iter++) {
			if ((*iter) == pListener) {
				sProtocolDataUpdateListenerList.erase(iter);
				return;
			}
		}
	}
}

static void notifyProtocolDataUpdate(const SProtocolData &data) {
	Mutex::Autolock _l(sLock);
	std::vector<OnProtocolDataUpdateFun>::const_iterator iter = sProtocolDataUpdateListenerList.begin();
	for (; iter != sProtocolDataUpdateListenerList.end(); iter++) {
		(*iter)(data);
	}
}

static SProtocolData sProtocolData;

SProtocolData& getProtocolData() {
	return sProtocolData;
}

extern bool isSuccessSent;
extern Mutex rLock;

static void replyACK(){
	BYTE dataBuf[] = {0xFF};
	UartContext::sendTo(UART_TTYS0, dataBuf, 1);
}
static void replyNACK(){
	BYTE dataBuf[] = {0xF0};
	UartContext::sendTo(UART_TTYS0, dataBuf, 1);
}
static void replyNSupport(){
	BYTE dataBuf[] = {0xF3};
	UartContext::sendTo(UART_TTYS0, dataBuf, 1);
}

/**
 * 获取校验码
 * Get checksum code
 */
BYTE getCheckSum(const BYTE *pData, int len) {
	int sum = 0;
	for (int i = 1; i < len; ++i) {
		sum += pData[i];
	}

	return  (sum ^ 0xFF); //只对低8位进行翻转
}

/**
 * 解析每一帧数据
 * Parse each frame of data
 */
static void procParse(const BYTE *pData, UINT len) {
	const BYTE *Data = &pData[3];
    switch (pData[1]) {
        case (BYTE)(DataTypeCode::Vehicle):{
        	sProtocolData.ACC.byte = Data[0];
        	sProtocolData.door.byte = Data[1];
        	sProtocolData.gear = GET_LOW_BITS(Data[2], 3);
        	sProtocolData.gearRank = GET_TWO_BITS(Data[2], 4);
        	sProtocolData.ShiftLever = GET_BIT(Data[2], 7);
        	sProtocolData.rotation_ = GET_LOW_N_BITS_TWOBYTES(Data[3],Data[4],12);
        	sProtocolData.speed_ = Data[5];
        }break;
        case (BYTE)(DataTypeCode::TempPressure):{
        	sProtocolData.CoolantTemp = MAKEWORD(Data[0],Data[1]);
        	sProtocolData.TransmissionTemp = MAKEWORD(Data[2],Data[3]);
        	sProtocolData.OutdoorTemp = MAKEWORD(Data[4],Data[5]);
        	sProtocolData.EngineOilTemp = MAKEWORD(Data[6],Data[7]);
        	sProtocolData.OilPressure = MAKEWORD(Data[8],Data[9]);
        	sProtocolData.Turbinepressure = Data[10];
        }break;
        case (BYTE)(DataTypeCode::FaultIndicator):{
        	sProtocolData.pilotLamp0.byte = Data[0];
        	sProtocolData.pilotLamp1.byte = Data[1];
        	sProtocolData.pilotLamp2.byte = Data[2];

        }break;
        case (BYTE)(DataTypeCode::WarningMessage):{
        	sProtocolData.prompt0.byte = Data[0];
        	sProtocolData.prompt1.byte = Data[1];
        	sProtocolData.prompt2.byte = Data[2];
        	sProtocolData.countdownACC = GET_LOW_BITS(Data[0], 4);
        	sProtocolData.countdownShut = GET_LOW_BITS(Data[1], 4);
        	sProtocolData.CalibrateGyro = GET_TWO_BITS(Data[3], 4);
        	sProtocolData.CalibrateCompass = GET_TWO_BITS(Data[3], 6);
        	sProtocolData.TireMonitoring = GET_BIT(Data[3], 0);
        	sProtocolData.TrailerBrake = GET_BIT(Data[3], 1);
        }break;
        case (BYTE)(DataTypeCode::FuelRange):{
        	sProtocolData.RangeEndurance = MAKEWORD(Data[0],Data[1]);
        	sProtocolData.Oil_percentage = Data[2];

        }break;
        case (BYTE)(DataTypeCode::TotalMileage):{
        	sProtocolData.odo = COMBINE_THREE_BYTES(Data[0],Data[1],Data[2]);

        }break;
        case (BYTE)(DataTypeCode::TripMileage):{
        	sProtocolData.SubtotalMileage1 = COMBINE_THREE_BYTES(Data[0],Data[1],Data[2]);
        	sProtocolData.TravelTime1 = MAKEWORD(Data[3],Data[4]);
        	sProtocolData.AverageFueL1 = MAKEWORD(Data[5],Data[6]);
        	sProtocolData.AverageVelocity1 = Data[7];

        	sProtocolData.SubtotalMileage2 = COMBINE_THREE_BYTES(Data[8],Data[9],Data[10]);
        	sProtocolData.TravelTime2 = MAKEWORD(Data[11],Data[12]);
        	sProtocolData.AverageFueL2 = MAKEWORD(Data[13],Data[14]);
        	sProtocolData.AverageVelocity2 = Data[15];

        }break;
        case (BYTE)(DataTypeCode::FuelConsump):{
        	sProtocolData.AverageFuel = MAKEWORD(Data[0],Data[1]);

        	sProtocolData.FuelHistory1 = MAKEWORD(Data[2],Data[3]);
        	sProtocolData.FuelHistory2 = MAKEWORD(Data[4],Data[5]);
        	sProtocolData.FuelHistory3 = MAKEWORD(Data[6],Data[7]);
        	sProtocolData.FuelHistory4 = MAKEWORD(Data[8],Data[9]);
        	sProtocolData.FuelHistory5 = MAKEWORD(Data[10],Data[11]);
        	sProtocolData.FuelHistory6 = MAKEWORD(Data[12],Data[13]);

        }break;
        case (BYTE)(DataTypeCode::TruckInfo):{
        	sProtocolData.EngineHours = MAKEWORD(Data[1],Data[2]);
        	sProtocolData.EngineIdleHours = MAKEWORD(Data[3],Data[4]);
        	sProtocolData.oilLife = Data[0];
        	sProtocolData.MyKeys = GET_LOW_BITS(Data[5], 3);
        	sProtocolData.AdminKeys = GET_HIGH_BITS(Data[5], 4);

        }break;
        case (BYTE)(DataTypeCode::BodyInfo):{
        	size_t i;
        	for (i = 0; i < 17; ++i) {
        		sProtocolData.VIN[i] = static_cast<char>(Data[i]);
        	}
        	sProtocolData.VIN[i] = '\0'; // 添加字符串结束符

        }break;
        case (BYTE)(DataTypeCode::TrailerInfo):{
        	sProtocolData.TrailerDistance = MAKEWORD(Data[3],Data[4]);
        	sProtocolData.TrailerAngle1 = GET_LOW_BITS(Data[1], 6);
        	sProtocolData.TrailerAngle2 = GET_LOW_BITS(Data[2], 6);
        	sProtocolData.TrailerDirection1 = GET_BIT(Data[1], 7);
        	sProtocolData.TrailerDirection2 = GET_BIT(Data[2], 7);
        	sProtocolData.Gain = Data[0];
        	sProtocolData.TrailerInfo.byte = Data[5];
        	sProtocolData.TrailerEffort = GET_TWO_BITS(Data[5], 4);
        	sProtocolData.TrailerType = GET_TWO_BITS(Data[5], 6);

        }break;
        case (BYTE)(DataTypeCode::OffroadInfo):{
        	sProtocolData.TrailerType = GET_TWO_BITS(Data[0], 0);
        	sProtocolData.TrailerType = GET_TWO_BITS(Data[0], 2);

        	sProtocolData.Oil_percentage = Data[1];
        	sProtocolData.Oil_percentage = Data[2];
        	sProtocolData.Oil_percentage = Data[3];
        	sProtocolData.Oil_percentage = Data[4];
        	sProtocolData.FRpitchAngle = GET_LOW_BITS(Data[5], 6);
        	sProtocolData.LRrollAngle = GET_LOW_BITS(Data[6], 6);
        	sProtocolData.ElevationAngle = GET_BIT(Data[5], 7);
        	sProtocolData.RollAngle = GET_BIT(Data[6], 7);
        	sProtocolData.compass = GET_LOW_BITS(Data[7], 4);

        }break;
        case (BYTE)(DataTypeCode::TirePressure):{
        	sProtocolData.FL_TP = MAKEWORD(Data[0],Data[1]);
        	sProtocolData.FR_TP = MAKEWORD(Data[2],Data[3]);
        	sProtocolData.RL_TP = MAKEWORD(Data[4],Data[5]);
        	sProtocolData.RR_TP = MAKEWORD(Data[6],Data[7]);
        }break;
        case (BYTE)(DataTypeCode::CruiseControl):{
        	sProtocolData.CruiseBool = GET_LOW_BITS(Data[0], 7);
        	sProtocolData.CruiseSpeed = Data[1];
        }break;
        case (BYTE)(DataTypeCode::SettingInfo):{
        	sProtocolData.SetInfo0.byte = Data[0];

        	sProtocolData.SetInfo2.byte = Data[2];
        	sProtocolData.SetInfo3.byte = Data[3];
        	sProtocolData.SetInfo4.byte = Data[4];
        	sProtocolData.AutolampDelay = GET_LOW_BITS(Data[1], 6);
        	sProtocolData.DaytimeLight = GET_BIT(Data[1], 7);

        	sProtocolData.PowerRunningBoards = GET_TWO_BITS(Data[2], 4);
        	sProtocolData.RemoteStart = GET_TWO_BITS(Data[3], 2);

        	sProtocolData.language = GET_LOW_BITS(Data[5], 4);
        	sProtocolData.Backlight = GET_LOW_BITS(Data[6], 6);
        }break;

        case (BYTE)(DataTypeCode::ControlKey):{
        	sProtocolData.pre_status = sProtocolData.status;	//上一个的按键状态
        	sProtocolData.status = (key_status)(GET_LOW_BITS(Data[0], 2));

        	if(sProtocolData.status){
        		//只有当前按键值有效才保存
        		sProtocolData.operation = (key_operation_e)(Data[1]);
        	}
        }break;
        case (BYTE)(DataTypeCode::RemoteKey):{
        	sProtocolData.KeyCtrl = (Data[0]);
        }break;
        case (BYTE)(DataTypeCode::DateTime):{
        	sProtocolData.Year = Data[0];
        	sProtocolData.Month = Data[1];
        	sProtocolData.Day = Data[2];
        	sProtocolData.Hour = Data[3];
        	sProtocolData.Minute = Data[4];
        	sProtocolData.Second = Data[5];
        }break;
        case (BYTE)(DataTypeCode::Upgrade_SLAVE):{
        	UPGRADEFILE->ProcessUpgrade(Data);
        }break;

        default:
        	replyNSupport();
    }


    // Notify protocol data update
	notifyProtocolDataUpdate(sProtocolData);
}

// 创建一个包含所有枚举值的静态数组
static const uint8_t validDataTypeCodes[] = {
		(BYTE)(DataTypeCode::Vehicle),
		(BYTE)(DataTypeCode::TempPressure),
		(BYTE)(DataTypeCode::FaultIndicator),
		(BYTE)(DataTypeCode::WarningMessage),
		(BYTE)(DataTypeCode::FuelRange),
		(BYTE)(DataTypeCode::TotalMileage),
		(BYTE)(DataTypeCode::TripMileage),
		(BYTE)(DataTypeCode::FuelConsump),
		(BYTE)(DataTypeCode::TruckInfo),
		(BYTE)(DataTypeCode::BodyInfo),
		(BYTE)(DataTypeCode::TrailerInfo),
		(BYTE)(DataTypeCode::OffroadInfo),
		(BYTE)(DataTypeCode::TirePressure),
		(BYTE)(DataTypeCode::CruiseControl),
		(BYTE)(DataTypeCode::SettingInfo),
//    (DataTypeCode::SetOperation),
//    (DataTypeCode::ControlCommand),
		(BYTE)(DataTypeCode::ControlKey),
		(BYTE)(DataTypeCode::RemoteKey),
		(BYTE)(DataTypeCode::DateTime),
//    (DataTypeCode::Request),
		(BYTE)(DataTypeCode::Upgrade_SLAVE)
//    (DataTypeCode::Update_HOST)
};

// 判断函数
static bool isValidDataTypeCode(uint8_t value) {
	return std::find(std::begin(validDataTypeCodes), std::end(validDataTypeCodes),
	                     value) != std::end(validDataTypeCodes);
}

static bool isReplyACK(uint8_t value) {
    return (value == 0xFF || value == 0xF0 || value == 0xF3 || value == 0xFC);
}
/**
 * 功能：解析协议
 * Function: Parse protocol
 * 参数：pData 协议数据，len 数据长度
 * Parameters: pData - protocol data, len - data length
 * 返回值：实际解析协议的长度
 * Return value: the length of the actual resolution protocol
 */
int parseProtocol(const BYTE *pData, UINT len) {
	UINT remainLen = len;	// 剩余数据长度 Remaining data length
	UINT dataLen;	// 数据包长度 Packet length
 	UINT frameLen;	// 帧长度 Frame length

 	static const BYTE *pACK = pData;
	/**
	 * 解析出每一帧的数据
	 */
	while (remainLen >= 2) {
		// Find the data header of a frame of data
		while ((remainLen >= 2) && (pData[0] != CMD_HEAD1) && isValidDataTypeCode(pData[1]) ) {
			pACK = pData;
			pData++;
			remainLen--;
			continue;
		}
//		for(int i = 0;i < remainLen;i++){
//			LOGE("----%X \n",pData[i]);
//		}
		if(isReplyACK(*pACK)){
			Mutex::Autolock lock(rLock);
			isSuccessSent = true;
		}
		if (remainLen < DATA_PACKAGE_MIN_LEN) {
			break;
		}

		dataLen = pData[2];
		frameLen = dataLen + DATA_PACKAGE_MIN_LEN;
		if (frameLen > remainLen) {
			// Incomplete data packet
			break;
		}

		// 打印一帧数据，需要时在CommDef.h文件中打开DEBUG_PRO_DATA宏
    // To print a data of frame, open the DEBUG_PRO_DATA macro in the CommDef.h file when needed
#ifdef DEBUG_PRO_DATA
		for (UINT i = 0; i < frameLen; ++i) {
			LOGD("%x ", pData[i]);
		}
		LOGD("\n");
#endif

		// 支持checksum校验，需要时在CommDef.h文件中打开PRO_SUPPORT_CHECK_SUM宏
    // Support checksum verification, open the PRO_SUPPORT_CHECK_SUM macro in CommDef.h file when needed
#ifdef PRO_SUPPORT_CHECK_SUM
		// 检测校验码 Checksum
		if (getCheckSum(pData, frameLen - 1) == pData[frameLen - 1]) {
			// 解析一帧数据
			replyACK();
			procParse(pData, frameLen);
		} else {
			replyNACK();
			LOGE("CheckSum error!!!\n",pData);
		}
#else
		// 解析一帧数据
    // Parse a data of frame
		procParse(pData, frameLen);
#endif

		pData += frameLen;
		remainLen -= frameLen;
	}

	return len - remainLen;
}
